/**
 * 版权所有(C)，上海海鼎信息工程股份有限公司，2008，所有权利保留。
 * 
 * 项目名：	rumba
 * 文件名：	GWTModuleDispatcher.java
 * 模块说明：	
 * 修改历史：
 * Feb 4, 2008 - lxm - 创建。
 */
package com.hd123.rumba.request;

import java.util.ArrayList;
import java.util.List;

import com.hd123.rumba.exception.BusinessException;
import com.hd123.rumba.log.RumbaLogger;
import com.hd123.rumba.util.ObjectUtil;

/**
 * GWT模块调度器。
 * <p>
 * 
 * 其最主要的功能是提供了一个策略算法，将一个指定的相对路径转换为一个可以被实际使用调用gwt模块的绝对路径。
 * <p>
 * 
 * 这是一个单件（Singleton）。
 * <p>
 * 
 * 在一般情况下，在使用前应该先通过调用{@link #config}方法初始化配置信息。然后将可以通过调用{@link #getModuleURL}方法将
 * 一个相对的gwt模块调用URL转换为一个完整的gwt模块调用URL。
 * 
 * @author lxm
 * 
 */
public class GWTModuleDispatcher {

  private static GWTModuleDispatcher instance = null;

  /**
   * 构造函数
   * 
   */
  private GWTModuleDispatcher() {
    super();
  }

  /**
   * 取得对象实例。
   * 
   * @return
   * @throws BusinessException
   */
  public static GWTModuleDispatcher getInstance() throws BusinessException {
    if (instance == null) {
      instance = new GWTModuleDispatcher();
      instance.config(GWTConstants.PREFIX, GWTConstants.PACKAGES);
    }
    return instance;
  }

  /**
   * 所有访问gwt模块地址的前缀。
   * <p>
   * 
   * 例如以下地址中的“<code>www</code>”：<br>
   * <code>http://localhost/rumba/www/com.hd123.rumba.get.entry.Entry/Entry.html</code>
   */
  private String prefix = null;
  /**
   * 被列为搜索范围的gwt模块所在包列表。
   */
  private List<String> packageList = new ArrayList();

  /**
   * 写入配置信息。
   * <p>
   * 
   * 一般情况下无需调用，系统将在对象构造的时候从{@link GWTConstants}中取得有关的配置信息。
   * 
   * @param prefix
   *          指定的gwt模块地址的前缀。
   * @param packages
   *          指定的包含了所有被列为被搜索范围的gwt模块所在包的字符串，其中各个包之间用分号字符“;”分隔，并且在字符串中的顺序
   *          决定了搜索匹配的优先关系，具体说排在后面的将被优先匹配。
   * @throws BusinessException
   */
  public void config(String prefix, String packages) throws BusinessException {
    ObjectUtil.checkParameterNotNull(prefix, "prefix");
    ObjectUtil.checkParameterNotNull(packages, "packages");

    this.prefix = prefix;

    // packages -> this.packageList，顺序反转。
    this.packageList.clear();
    String[] words = packages.split(";");
    for (int i = words.length - 1; i >= 0; i--) {
      String word = words[i].trim();
      if ("".equals(word))
        continue;
      if (word.endsWith("."))
        word = word.substring(0, word.length() - 1);
      this.packageList.add(word);
    }
    if (this.packageList.size() == 0)
      RumbaLogger.getLogger(this.getClass()).warn("缺少有效的搜索路径，请检查GWTConstants.PACKAGES相关配置。");
  }

  /**
   * 取得gwt模块访问URL。
   * 
   * @param relaModuleURL
   *          指定相对gwt模块url。例如：<code>entry.Entry/Entry.html</code>。
   * @return 返回完整的gwt模块访问URL。例如：<code>www/com.hd123.rumba.gwt.entry.Entry/Entry.html</code>。
   * @throws BusinessException
   */
  public String getModuleURL(String relaModuleURL) throws BusinessException {
    ObjectUtil.checkParameterNotNull(relaModuleURL, "relaModuleURL");
    if (this.prefix == null)
      throw new BusinessException("配置信息不正确。");

    ModuleInfo info = this.decodeModuleURL(relaModuleURL);
    
    if (this.isValidModule(info.module)) {
      info.prefix = this.prefix;
      return info.toURL();
    }

    // 顺序检查packageList中的所有package。
    for (String pkg : this.packageList) {
      String module = info.module;
      if (module.startsWith("."))
        module = pkg + module;
      else
        module = pkg + "." + module;
      if (this.isValidModule(module)) {
        info.module = module;
        info.prefix = this.prefix;
        break;
      }
    }

    return info.toURL();
  }

  private ModuleInfo decodeModuleURL(String moduleURL) throws BusinessException {
    ModuleInfo info = new ModuleInfo();

    // 用字符“/”将传入参数moduleURL分隔为若干段。
    String[] ss = moduleURL.split("/");

    // 跳过前面所有的空串。
    int idx = 0;
    while (idx < ss.length) {
      String word = ss[idx];
      if (!"".equals(word.trim()))
        break;
      idx++;
    }

    // 取得prefix和module部分。
    if (this.prefix.equals(ss[idx])) {
      info.prefix = ss[idx];
      idx++;
      if (idx >= ss.length)
        return info;
    }
    info.module = ss[idx];
    idx++;

    // 取得file部分。
    StringBuffer sb = new StringBuffer();
    while (idx < ss.length) {
      String word = ss[idx];
      if (sb.length() > 0)
        sb.append("/");
      sb.append(word);
      idx++;
    }
    info.file = sb.toString();

    return info;
  }

  private boolean isValidModule(String module) {
    String fileName = "/" + module.replace('.', '/') + ".gwt.xml";
    return this.getClass().getResourceAsStream(fileName) != null;
  }

  private class ModuleInfo {
    public String prefix = "";
    public String module = "";
    public String file = "";

    public String toURL() {
      StringBuffer sb = new StringBuffer();
      sb.append(prefix);
      if (!"".equals(sb.toString()))
        sb.append("/");
      sb.append(module);
      if (!"".equals(sb.toString()))
        sb.append("/");
      sb.append(file);
      return sb.toString();
    }
  }

}
